package com.hwtx.protocol.dubbo.serialize.hessian;


import com.hwtx.protocol.dubbo.serialize.ObjectInput;
import com.hwtx.protocol.dubbo.serialize.ObjectOutput;
import com.hwtx.protocol.dubbo.serialize.ReflectUtils;
import com.hzgj.bcl.soa.interceptor.Serialization;
import com.hzgj.bcl.soa.rpc.message.Message;
import com.hzgj.bcl.soa.rpc.message.MethodCall;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Map;

import static com.hwtx.protocol.dubbo.code.DubboConstants.MESSAGE_HEADER_DUBBO_BIZID;
import static com.hwtx.protocol.dubbo.code.DubboConstants.MESSAGE_HEADER_DUBBO_TRACEID;
import static com.hwtx.protocol.dubbo.code.DubboConstants.MESSAGE_HEADER_DUBBO_WHOIM;
import static com.hwtx.protocol.dubbo.code.DubboConstants.RESPONSE_NULL_VALUE;
import static com.hwtx.protocol.dubbo.code.DubboConstants.RESPONSE_VALUE;
import static com.hwtx.protocol.dubbo.code.DubboConstants.RESPONSE_WITH_EXCEPTION;
import static com.hzgj.bcl.soa.util.Constants.MESSAGE_HEADER_ATTACHMENT;
import static com.hzgj.bcl.soa.util.Constants.MESSAGE_HEADER_METHOD_ARGS;
import static com.hzgj.bcl.soa.util.Constants.MESSAGE_HEADER_METHOD_PARAMETERTYPES;
import static com.hzgj.bcl.soa.util.Constants.MESSAGE_HEADER_SERVICE_VERSION;

public class Hessian2Serialization implements Serialization {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public String getName() {
        return "dubbo";
    }

    @Override
    public byte getId() {
        return 2;
    }

    @Override
    public byte[] write(Message message) throws Exception {

        ByteArrayOutputStream output = new ByteArrayOutputStream(8092);
        ObjectOutput out = new Hessian2ObjectOutput(output);
        try {
            if (message.isRequest()) {
                out.writeUTF("2.0.0"); //dubbo version
                out.writeUTF(message.getService()); //path
                out.writeUTF((String) message.getHeader(MESSAGE_HEADER_SERVICE_VERSION)); //version
                out.writeUTF(message.getMethod());
                out.writeUTF(ReflectUtils.getDesc((Class<?>[]) message.getHeader(MESSAGE_HEADER_METHOD_PARAMETERTYPES)));
                Object[] args = (Object[]) message.getHeader(MESSAGE_HEADER_METHOD_ARGS);
                if (args != null) {
                    for (int i = 0; i < args.length; i++) {
                        out.writeObject(args[i]);
                    }
                }
                out.writeObject(message.getHeader(MESSAGE_HEADER_ATTACHMENT));
                out.flushBuffer();
            } else {
                Object result = message.getPayload();

                if (result instanceof Throwable) {
                    out.writeByte(RESPONSE_WITH_EXCEPTION);
                    out.writeObject(result);
                } else {
                    if (result == null) {
                        out.writeByte(RESPONSE_NULL_VALUE);
                    } else {
                        out.writeByte(RESPONSE_VALUE);
                        out.writeObject(result);
                    }
                }
                out.flushBuffer();
            }
        } catch (Exception e) {
            logger.error("Hessian2Serialization write", e);
            throw e;
        } finally {
            try {
                output.close();
            } catch (IOException e) {
                logger.error("{}", e);
            }
        }
        return output.toByteArray();

    }

    @Override
    public Object read(Message message) throws Exception {

        ByteArrayInputStream input = new ByteArrayInputStream((byte[]) message.getPayload());
        ObjectInput objectInput = new Hessian2ObjectInput(input);

        try {
            if (message.isRequest()) {
                //dubbo.version
                objectInput.readUTF();
                //path_key
                String path = objectInput.readUTF();
                //version
                objectInput.readUTF();

                String methodName = objectInput.readUTF();
                Object[] args = new Object[0];
                //参数类型未使用
                Class<?>[] pts;
                String desc = objectInput.readUTF();
                if (desc.length() == 0) {
                } else {
                    pts = ReflectUtils.desc2classArray(desc);
                    args = new Object[pts.length];
                    for (int i = 0; i < args.length; i++) {
                        try {
                            args[i] = objectInput.readObject(pts[i]);
                        } catch (Exception e) {
                            if (logger.isWarnEnabled()) {
                                logger.warn("Decode argument failed: " + e.getMessage(), e);
                            }
                        }
                    }
                }
                MethodCall methodCall1 = new MethodCall(methodName, args);
                methodCall1.setService(path);

                Map<String, String> map = (Map<String, String>) objectInput.readObject(Map.class);
                if (map != null && map.size() > 0) {
                    methodCall1.setBizId(map.get(MESSAGE_HEADER_DUBBO_BIZID));
                    methodCall1.setClientName(map.get(MESSAGE_HEADER_DUBBO_WHOIM));
                    methodCall1.setContextId(map.get(MESSAGE_HEADER_DUBBO_TRACEID));
                }
                return methodCall1;
            } else {
                //flag
                //RESPONSE_NULL_VALUE
                //RESPONSE_VALUE
                //RESPONSE_WITH_EXCEPTION
                objectInput.readByte();
                return objectInput.readObject();
            }
        } catch (Exception e) {
            logger.error("Hessian2Serialization read", e);
            throw e;
        } finally {
            try {
                input.close();
            } catch (IOException e) {
            }
        }
    }
}
